﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace ConstantCMapEditor3
{
    abstract public class Modifier
    {
        public delegate void ModifierDoneHandler(Modifier modifier);
        public ModifierDoneHandler ModifierDoneEvent = null;
        public Modifier(float startTime, float endTime)
        {
            StartTime = startTime;
            EndTime = endTime;
            Reset();
            Pause = false;
        }
        virtual public void Reset()
        {
            CurrentTime = 0;
            Finished = false;
        }
        public bool Finished
        {
            get;
            set;
        }

        public bool Pause
        {
            get;
            set;
        }
        virtual public void Update(GameTime time, Scene target)
        {
            if (Pause) return;

            if (!Finished)
            {
                if (CurrentTime < EndTime) CurrentTime += time.ElapsedGameTime.Milliseconds;

                if (CurrentTime >= EndTime)
                {
                    CurrentTime = EndTime;
                    Finished = true;
                    if (ModifierDoneEvent != null)
                    {
                        ModifierDoneEvent.Invoke(this);
                    }
                    UpdateImplement(1, target);
                }
                else if (CurrentTime >= StartTime && CurrentTime < EndTime)
                {
                    float currentDif = CurrentTime - StartTime;
                    float totalDif = EndTime - StartTime;

                    UpdateImplement(currentDif / totalDif, target);
                }
            }
        }
        abstract protected void UpdateImplement(float ratio, Scene target);
        abstract public Modifier Clone();
        virtual public float StartTime
        {
            get;
            set;
        }
        virtual public float EndTime
        {
            get;
            set;
        }
        public float CurrentTime
        {
            get;
            set;
        }
    }
    abstract public class SingleVariableModifier : Modifier
    {
        protected float _startValue;
        protected float _endValue;

        public SingleVariableModifier(float startValue, float endValue, float startTime, float endTime)
            : base(startTime, endTime)
        {
            _startValue = startValue;
            _endValue = endValue;
        }

        override protected void UpdateImplement(float ratio, Scene target)
        {
            float value = _endValue * ratio + _startValue * (1 - ratio);
            SingleUpdate(value, target);
        }

        abstract protected void SingleUpdate(float value, Scene target);
    }
    abstract public class SingleCustomModifier : SingleVariableModifier
    {
        public delegate double CustomFunctionHandler(double value);
        public CustomFunctionHandler CustomFunctionX;

        public SingleCustomModifier(float startAngle, float endAngle, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
            CustomFunctionX = Linear;
        }

        public SingleCustomModifier(float startAngle, float endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
            CustomFunctionX = handler;
        }

        override protected void SingleUpdate(float value, Scene target)
        {
            SingleCustomUpdate((float)CustomFunctionX.Invoke(value), target);
        }

        abstract protected void SingleCustomUpdate(float value, Scene target);

        static public double Linear(double value)
        {
            return value;
        }
    }

    public class RangeModifier : SingleCustomModifier
    {
        public RangeModifier(float startAngle, float endAngle, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {

        }

        public RangeModifier(float startAngle, float endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, handler, startTime, endTime)
        {

        }

        override protected void SingleCustomUpdate(float value, Scene target)
        {
            LightSourceScene light = (LightSourceScene)target;
            light.Width = value;
        }

        override public Modifier Clone()
        {
            return new RangeModifier(_startValue, _endValue, CustomFunctionX, StartTime, EndTime);
        }
    }
    public class IntensityModifier : SingleCustomModifier
    {
        public IntensityModifier(float startAngle, float endAngle, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {

        }

        public IntensityModifier(float startAngle, float endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, handler, startTime, endTime)
        {

        }

        override protected void SingleCustomUpdate(float value, Scene target)
        {
            LightSourceScene light = (LightSourceScene)target;
            light.Intensity = value;
        }

        override public Modifier Clone()
        {
            return new IntensityModifier(_startValue, _endValue, CustomFunctionX, StartTime, EndTime);
        }
    }


    abstract public class DoubleVariableModifier : Modifier
    {
        protected Vector2 _startValue;
        protected Vector2 _endValue;

        public DoubleVariableModifier(Vector2 startValue, Vector2 endValue, float startTime, float endTime)
            : base(startTime, endTime)
        {
            _startValue = startValue;
            _endValue = endValue;
        }

        override protected void UpdateImplement(float ratio, Scene target)
        {
            Vector2 QudraRatio = new Vector2(ratio);
            Vector2 value = _endValue * QudraRatio + _startValue * (Vector2.One - QudraRatio);
            DoubleUpdate(value, target);
        }

        abstract protected void DoubleUpdate(Vector2 value, Scene target);
    }
    abstract public class DoubleCustomModifier : DoubleVariableModifier
    {
        public delegate double CustomFunctionHandler(double value);
        public CustomFunctionHandler CustomFunctionX;
        public CustomFunctionHandler CustomFunctionY;

        public DoubleCustomModifier(Vector2 startAngle, Vector2 endAngle, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
            CustomFunctionX = Linear;
            CustomFunctionY = Linear;
        }

        public DoubleCustomModifier(Vector2 startAngle, Vector2 endAngle, CustomFunctionHandler handler, CustomFunctionHandler handler2, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
            CustomFunctionX = handler;
            CustomFunctionY = handler2;
        }

        public DoubleCustomModifier(Vector2 startAngle, Vector2 endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
            CustomFunctionX = handler;
            CustomFunctionY = handler;
        }

        override protected void DoubleUpdate(Vector2 value, Scene target)
        {
            DoubleCustomUpdate(new Vector2((float)CustomFunctionX.Invoke(value.X), (float)CustomFunctionY.Invoke(value.Y)), target);
        }

        abstract protected void DoubleCustomUpdate(Vector2 value, Scene target);
        static public double Linear(double value)
        {
            return value;
        }
    }


    abstract public class TripleVariableModifier : Modifier
    {
        protected Vector3 _startValue;
        protected Vector3 _endValue;

        public TripleVariableModifier(Vector3 startValue, Vector3 endValue, float startTime, float endTime)
            : base(startTime, endTime)
        {
            _startValue = startValue;
            _endValue = endValue;
        }

        override protected void UpdateImplement(float ratio, Scene target)
        {
            Vector3 QudraRatio = new Vector3(ratio);
            Vector3 value = _endValue * QudraRatio + _startValue * (Vector3.One - QudraRatio);
            DoubleUpdate(value, target);
        }

        abstract protected void DoubleUpdate(Vector3 value, Scene target);
    }
    abstract public class TripleCustomModifier : TripleVariableModifier
    {
        public delegate double CustomFunctionHandler(double value);
        public CustomFunctionHandler CustomFunctionX;
        public CustomFunctionHandler CustomFunctionY;
        public CustomFunctionHandler CustomFunctionZ;

        public TripleCustomModifier(Vector3 startAngle, Vector3 endAngle, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
            CustomFunctionX = Linear;
            CustomFunctionY = Linear;
            CustomFunctionZ = Linear;
        }

        public TripleCustomModifier(Vector3 startAngle, Vector3 endAngle, CustomFunctionHandler handler, CustomFunctionHandler handler2, CustomFunctionHandler handler3, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
            CustomFunctionX = handler;
            CustomFunctionY = handler2;
            CustomFunctionZ = handler3;
        }

        public TripleCustomModifier(Vector3 startAngle, Vector3 endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
            CustomFunctionX = handler;
            CustomFunctionY = handler;
            CustomFunctionZ = handler;
        }

        override protected void DoubleUpdate(Vector3 value, Scene target)
        {
            TripleCustomUpdate(new Vector3((float)CustomFunctionX.Invoke(value.X), (float)CustomFunctionY.Invoke(value.Y), (float)CustomFunctionZ.Invoke(value.Z)), target);
        }

        abstract protected void TripleCustomUpdate(Vector3 value, Scene target);

        static public double Linear(double value)
        {
            return value;
        }
    }

    abstract public class QudraVariableModifier : Modifier
    {
        protected Vector4 _startValue;
        protected Vector4 _endValue;

        public QudraVariableModifier(Vector4 startValue, Vector4 endValue, float startTime, float endTime)
            : base(startTime, endTime)
        {
            _startValue = startValue;
            _endValue = endValue;
        }

        override protected void UpdateImplement(float ratio, Scene target)
        {
            Vector4 QudraRatio = new Vector4(ratio);
            Vector4 value = _endValue * QudraRatio + _startValue * (Vector4.One - QudraRatio);
            QudraUpdate(value, target);
        }

        abstract protected void QudraUpdate(Vector4 value, Scene target);
    }
    abstract public class QudraCustomModifier : QudraVariableModifier
    {
        public delegate double CustomFunctionHandler(double value);
        public CustomFunctionHandler CustomFunctionX;
        public CustomFunctionHandler CustomFunctionY;
        public CustomFunctionHandler CustomFunctionZ;
        public CustomFunctionHandler CustomFunctionA;

        public QudraCustomModifier(Vector4 startAngle, Vector4 endAngle, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
            CustomFunctionX = Linear;
            CustomFunctionY = Linear;
            CustomFunctionZ = Linear;
            CustomFunctionA = Linear;
        }

        public QudraCustomModifier(Vector4 startAngle, Vector4 endAngle, CustomFunctionHandler handler, CustomFunctionHandler handler2, CustomFunctionHandler handler3, CustomFunctionHandler handler4, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
            CustomFunctionX = handler;
            CustomFunctionY = handler2;
            CustomFunctionZ = handler3;
            CustomFunctionA = handler4;
        }

        public QudraCustomModifier(Vector4 startAngle, Vector4 endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
            CustomFunctionX = handler;
            CustomFunctionY = handler;
            CustomFunctionZ = handler;
            CustomFunctionA = handler;
        }

        override protected void QudraUpdate(Vector4 value, Scene target)
        {
            QuadraCustomUpdate(new Vector4((float)CustomFunctionX.Invoke(value.X), (float)CustomFunctionY.Invoke(value.Y), (float)CustomFunctionZ.Invoke(value.Z), (float)CustomFunctionA.Invoke(value.W)), target);
        }

        abstract protected void QuadraCustomUpdate(Vector4 value, Scene target);

        static public double Linear(double value)
        {
            return value;
        }
    }

    public class RotateModifier : SingleCustomModifier
    {
        public RotateModifier(float startAngle, float endAngle, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {

        }

        public RotateModifier(float startAngle, float endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, handler, startTime, endTime)
        {

        }

        override protected void SingleCustomUpdate(float value, Scene target)
        {
            target.Rotate = value;
        }

        override public Modifier Clone()
        {
            return new RotateModifier(_startValue, _endValue, CustomFunctionX, StartTime, EndTime);
        }
    }
    public class SingleVariableScripter : SingleCustomModifier
    {
        public delegate void VariableEventHandler(float value);
        public VariableEventHandler VariableEvent = null;

        public SingleVariableScripter(float startValue, float EndValue, float startTime, float endTime)
            : base(startValue, EndValue, startTime, endTime)
        {

        }

        public SingleVariableScripter(float startAngle, float endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, handler, startTime, endTime)
        {

        }

        override protected void SingleCustomUpdate(float value, Scene target)
        {
            if (VariableEvent != null)
            {
                VariableEvent.Invoke(value);
            }
        }
        override public Modifier Clone()
        {
            return new SingleVariableScripter(_startValue, _endValue, CustomFunctionX, StartTime, EndTime);
        }
    }
    public class ScaleXModifier : SingleCustomModifier
    {
        public ScaleXModifier(float startAngle, float endAngle, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {

        }

        public ScaleXModifier(float startAngle, float endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, handler, startTime, endTime)
        {

        }

        override protected void SingleCustomUpdate(float value, Scene target)
        {
            target.ScaleX = value;
        }

        override public Modifier Clone()
        {
            return new ScaleXModifier(_startValue, _endValue, CustomFunctionX, StartTime, EndTime);
        }
    }
    public class ScaleYModifier : SingleCustomModifier
    {
        public ScaleYModifier(float startAngle, float endAngle, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {

        }

        public ScaleYModifier(float startAngle, float endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, handler, startTime, endTime)
        {

        }

        override protected void SingleCustomUpdate(float value, Scene target)
        {
            target.ScaleY = value;
        }

        override public Modifier Clone()
        {
            return new ScaleYModifier(_startValue, _endValue, CustomFunctionX, StartTime, EndTime);
        }
    }


    public class ScaleModifier : DoubleCustomModifier
    {
        public ScaleModifier(Vector2 startScale, Vector2 endScale, float startTime, float endTime)
            : base(startScale, endScale, startTime, endTime)
        {
        }

        public ScaleModifier(Vector2 startAngle, Vector2 endAngle, CustomFunctionHandler handler, CustomFunctionHandler handler2, float startTime, float endTime)
            : base(startAngle, endAngle, handler, handler2, startTime, endTime)
        {
        }

        public ScaleModifier(Vector2 startAngle, Vector2 endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, handler, startTime, endTime)
        {
        }

        public ScaleModifier(float startScale, float endScale, CustomFunctionHandler handler, float startTime, float endTime)
            : base(new Vector2(startScale, startScale), new Vector2(endScale, endScale), handler, startTime, endTime)
        {
        }

        public ScaleModifier(float startScale, float endScale, float startTime, float endTime)
            : base(new Vector2(startScale, startScale), new Vector2(endScale, endScale), startTime, endTime)
        {
        }

        override protected void DoubleCustomUpdate(Vector2 value, Scene target)
        {
            target.ScaleX = value.X;
            target.ScaleY = value.Y;
        }

        override public Modifier Clone()
        {
            return new ScaleModifier(_startValue, _endValue, CustomFunctionX, CustomFunctionY, StartTime, EndTime);
        }
    }

    public class ColorModifier : QudraCustomModifier
    {
        public ColorModifier(Vector4 startAngle, Vector4 endAngle, float startTime, float endTime)
            : base(startAngle, endAngle, startTime, endTime)
        {
        }

        public ColorModifier(Vector4 startAngle, Vector4 endAngle, CustomFunctionHandler handler, CustomFunctionHandler handler2, CustomFunctionHandler handler3, CustomFunctionHandler handler4, float startTime, float endTime)
            : base(startAngle, endAngle, handler, handler2, handler3, handler4, startTime, endTime)
        {
        }

        public ColorModifier(Vector4 startAngle, Vector4 endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, handler, startTime, endTime)
        {
        }

        override protected void QuadraCustomUpdate(Vector4 value, Scene target)
        {
            target.SpriteColor = new Color(value);
        }

        override public Modifier Clone()
        {
            return new ColorModifier(_startValue, _endValue, CustomFunctionX, CustomFunctionY, CustomFunctionZ, CustomFunctionA, StartTime, EndTime);
        }
    }

    public class PositionModifier : DoubleCustomModifier
    {
        public PositionModifier(Vector2 startPosition, Vector2 endPosition, float startTime, float endTime)
            : base(startPosition, endPosition, startTime, endTime)
        {

        }

        public PositionModifier(Vector2 startAngle, Vector2 endAngle, CustomFunctionHandler handler, CustomFunctionHandler handler2, float startTime, float endTime)
            : base(startAngle, endAngle, handler, handler2, startTime, endTime)
        {
        }

        public PositionModifier(Vector2 startAngle, Vector2 endAngle, CustomFunctionHandler handler, float startTime, float endTime)
            : base(startAngle, endAngle, handler, startTime, endTime)
        {

        }

        override protected void DoubleCustomUpdate(Vector2 value, Scene target)
        {
            target.Position = value;
        }
        override public Modifier Clone()
        {
            return new PositionModifier(_startValue, _endValue, CustomFunctionX, CustomFunctionY, StartTime, EndTime);
        }
    }

    public class ComplexModifier : Modifier
    {
        List<Modifier> _modifierList;

        public ComplexModifier(List<Modifier> modifierList)
            : base(0, 1)
        {
            _modifierList = modifierList;

            // find End Time
            EndTime = 0;
            foreach (Modifier modifier in modifierList)
            {
                if (modifier.EndTime > EndTime)
                {
                    EndTime = modifier.EndTime;
                }
            }

            Reset();
        }
        override public void Reset()
        {
            base.Reset();
            if (_modifierList != null)
            {
                foreach (Modifier modifier in _modifierList)
                {
                    modifier.Reset();
                }
            }
        }
        private void UpdateAll(GameTime time, Scene target)
        {
            foreach (Modifier modifier in _modifierList)
            {
                modifier.Update(time, target);
            }
        }
        override public void Update(GameTime time, Scene target)
        {
            if (Pause) return;
            UpdateAll(time, target);
            CurrentTime += time.ElapsedGameTime.Milliseconds;
            if (CurrentTime >= EndTime)
            {
                if (ModifierDoneEvent != null) ModifierDoneEvent.Invoke(this);
            }

        }
        override protected void UpdateImplement(float ratio, Scene target)
        {
            // do nothing
        }
        override public Modifier Clone()
        {
            List<Modifier> temp = new List<Modifier>();

            foreach (Modifier modifier in _modifierList)
            {
                temp.Add(modifier.Clone());
            }

            return new ComplexModifier(temp);
        }
    }

    public class LoopModifier : Modifier
    {
        Modifier _loopTarget;
        int _loopTime;
        int _currentTime;

        public LoopModifier(Modifier loopTarget, int loopTime)
            : base(loopTarget.StartTime, loopTarget.EndTime)
        {
            //if (loopTarget is ComplexModifier) throw new Exception("bad....");

            _loopTarget = loopTarget;
            _loopTime = loopTime;
            _currentTime = 0;
            _loopTarget.ModifierDoneEvent += UpdateCheck;
        }

        override public void Reset()
        {
            base.Reset();
            _currentTime = 0;
            if (_loopTarget != null) _loopTarget.Reset();
        }

        override public void Update(GameTime time, Scene target)
        {
            if (Pause) return;
            if (!Finished)
            {
                _loopTarget.Update(time, target);
            }
        }

        private void UpdateCheck(Modifier modifier)
        {
            if (_loopTime > 0)
            {
                _currentTime++;

                if (_currentTime < _loopTime)
                {
                    modifier.Reset();
                }
                else
                {
                    Finished = true;
                    if (ModifierDoneEvent != null)
                    {
                        ModifierDoneEvent.Invoke(this);
                    }
                }
            }
            else
            {
                modifier.Reset();
            }
        }

        override protected void UpdateImplement(float ratio, Scene target)
        {
            // do nothing
        }

        override public Modifier Clone()
        {
            return new LoopModifier(_loopTarget.Clone(), _loopTime);
        }
    }
}
